﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;

public class AssetUpdateManager : MonoBehaviour
{
    public enum AssetUpdateState : int
    {
        NONE,
        Copy_Date,
        Small_Checking,//小包资源
        SMALL_PACKAGE_DOWNLOAD,
        SMALL_PACKAGE_MOVE,
        BIG_CHECKING,
        BIG_DOWNLOAD,
        WAIT_FOR_MOVE,
        BIG_MOVE,
        COMPLETE,
        ERROR,
    }


    public const string manifestFileName = "assetmanifest";
    public const string smallManifestFileName = "smallassetmanifest";
    private int targetVersion;
    private string baseUrl;

    private AssetFileTool serverAssetTool;
    private AssetFileTool smallServerAssetTool;

    private AssetFileTool localAssetTool;
    private AssetFileTool localSmallAssetTool;

    private List<AssetCell> unneceList;
    private List<AssetCell> lackList;
    private List<AssetCell> obbList;

    private List<AssetCell> errorList = new List<AssetCell>();

    private List<AssetCell> downloadList = new List<AssetCell>();
    private const int MAX_DOWNLOADING = 4;

    public int totalDownloadCount = 0;
    public int loadedCount = 0;
    public long totalSize = 0;
    public long loadedSize = 0;
    private long webLoadSize = 0;
    private ResourceUpdateMethod updateMethod = 0;
    public UserUpdateMethod userContinue = 0;//0默认，1容许，2暂停
    private float downloadTime = 0;
    private bool dialogOk = false;

    public float downloadSpeed
    {
        get
        {
            if (downloadTime == 0)
            {
                return 0;
            }
            if (isPause)
            {
                return 0;
            }
            return (webLoadSize >> 10) / downloadTime;
        }
    }

    public bool isPause
    {
        get
        {
            return userContinue == UserUpdateMethod.PAUSE/* || userContinue == UserUpdateMethod.NONE && IsMobileNet*/;
        }
    }
    //private Dictionary<string, float> speedDict = new Dictionary<string, float>();
    //private bool isMobile = true;

    public bool IsMobileNet
    {
        get
        {
            //return isMobile;
            return Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork;
        }
    }

    private bool preMobile = false;
    //public void OnGUI()
    //{
    //    if(GUI.Button(new Rect(0,100,100,50),"switch"))
    //    {
    //        isMobile = !isMobile;
    //    }
    //}

    public bool DataCopyComplete
    {
        get
        {
            return PlayerPrefs.GetInt("CopyDataFile", 0) == 0;
        }
        set
        {
            PlayerPrefs.SetInt("CopyDataFile", value ? 1 : 0);
        }
    }

    public string TmpDirectory
    {
        get
        {
#if UNITY_ANDROID
            return Application.persistentDataPath + "/tmp";
#elif UNITY_IPHONE
            return Application.persistentDataPath + "/tmp";
#else
            return Application.persistentDataPath + "/tmp";
#endif
        }
    }


    public string AbDirectory
    {
        get
        {
#if UNITY_ANDROID
            return Application.persistentDataPath;
#elif UNITY_IPHONE
            return Application.persistentDataPath;
#else
            return Application.persistentDataPath;
#endif
        }

    }

    [SerializeField]
    private AssetUpdateState state;

    public AssetUpdateState State
    {
        get
        {
            return state;
        }
    }

    public void BeginUpdate(ResourceUpdateMethod updateMethod)
    {
        enabled = true;
        this.targetVersion = int.Parse(GameConfig.Instance.serverResVersion);
        this.baseUrl = GameConfig.Instance.resUrl;
        state = AssetUpdateState.COPY_DATA;
        Debug.Log("Update Asset state: " + state);
        Debug.Log("temp cache path: " + AbDirectory);
        Debug.Log("base url: " + baseUrl);
        this.updateMethod = updateMethod;
        userContinue = UserUpdateMethod.PAUSE;
        dialogOk = false;
        StopAllCoroutines();
        StartCoroutine(CopyLocalDataFile(false));
    }

    public void Update()
    {
        switch (state)
        {
            case AssetUpdateState.NONE:
                enabled = false;
                break;
            case AssetUpdateState.COPY_DATA:
                break;
            case AssetUpdateState.SMALL_CHECKING:
                break;
            case AssetUpdateState.SMALL_PACKAGE_DOWNLOAD:
                if (!dialogOk)
                {
                    return;
                }
                if (downloadList.Count < MAX_DOWNLOADING)
                {
                    if (lackList.Count > 0)
                    {
                        AssetCell ac = lackList[0];
                        lackList.RemoveAt(0);
                        downloadList.Add(ac);
                        StartCoroutine(DownloadAsset(ac));
                    }
                    else if (obbList.Count > 0)
                    {
                        AssetCell ac = obbList[0];
                        obbList.RemoveAt(0);
                        downloadList.Add(ac);
                        StartCoroutine(DownloadAsset(ac));
                    }
                    else if (errorList.Count > 0)
                    {
                        AssetCell ac = errorList[0];
                        errorList.RemoveAt(0);
                        downloadList.Add(ac);
                        StartCoroutine(DownloadAsset(ac));
                    }
                    else if (downloadList.Count == 0)
                    {
                        state = AssetUpdateState.SMALL_PACKAGE_MOVE;
                        Debug.Log("Update Asset state: " + state);
                        StartCoroutine(MoveSmallPack());
                    }
                }

                break;
            case AssetUpdateState.SMALL_PACKAGE_MOVE:
                break;
            case AssetUpdateState.BIG_CHECKING:

                break;
            case AssetUpdateState.BIG_DOWNLOAD:
                //弹出用户提示框问用户是否更新
                int bigDataDownloadCount = 0;
                if (!preMobile && IsMobileNet)
                {
                    userContinue =  UserUpdateMethod.PAUSE;
                }
                preMobile = IsMobileNet;
                if (GameConfig.Instance.resMethod == ResourceUpdateMethod.FORCE_UPDATE && GameConfig.Instance.IsDownloadOver)
                {
                    bigDataDownloadCount = 4;
                }
                else if (Application.internetReachability == NetworkReachability.ReachableViaLocalAreaNetwork)
                {
                    if (userContinue == UserUpdateMethod.CONTINUE)
                    {
                        bigDataDownloadCount = 1;
                    }
                    else
                    {
                        bigDataDownloadCount = 0;
                    }
                }
                else if (userContinue == UserUpdateMethod.CONTINUE && Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork)
                {
                    bigDataDownloadCount = 1;
                }
                if (downloadList.Count > 0)
                {
                    downloadTime += Time.deltaTime;
                }
                while (downloadList.Count < bigDataDownloadCount)
                {
                    if (lackList.Count > 0)
                    {
                        AssetCell ac = lackList[0];
                        lackList.RemoveAt(0);
                        downloadList.Add(ac);
                        StartCoroutine(DownloadAsset(ac));
                    }
                    else if (obbList.Count > 0)
                    {
                        AssetCell ac = obbList[0];
                        obbList.RemoveAt(0);
                        downloadList.Add(ac);
                        StartCoroutine(DownloadAsset(ac));
                    }
                    else if (errorList.Count > 0)
                    {
                        AssetCell ac = errorList[0];
                        errorList.RemoveAt(0);
                        downloadList.Add(ac);
                        StartCoroutine(DownloadAsset(ac));
                    }
                    else if (downloadList.Count == 0)
                    {
                        bool shouldMove = false;
                        GameObject loginObj = GameObject.Find("/UI Root/loginUI");
                        if (loginObj != null)
                        {
                            Login.LoginManager lm = loginObj.GetComponent<Login.LoginManager>();
                            if (lm.GetCurrrentStateType() == Login.LoginState.SyncDataAndRes)
                            {
                                state = AssetUpdateState.BIG_MOVE;
                                Debug.Log("Update Asset state: " + state);
                                StartCoroutine(MoveBigPack());
                                shouldMove = true;
                            }
                        }
                        if (!shouldMove)
                        {
                            state = AssetUpdateState.WAIT_FOR_MOVE;
                            GameConfig.Instance.IsDownloadOver = true;
                            Debug.Log("Update Asset state: " + state);
                        }
                        break;
                    }
                    else
                    {
                        break;
                    }
                }
                break;
            case AssetUpdateState.WAIT_FOR_MOVE:
                break;
            case AssetUpdateState.BIG_MOVE:
                break;
            case AssetUpdateState.COMPLETE:

                enabled = false;
                break;
        }
    }



#region state oprator
    /// <summary>
    /// 拷贝包内数据文件
    /// </summary>
    /// <returns></returns>
    public IEnumerator CopyLocalDataFile(bool copyOnly)
    {
        yield return 1;
        if (!Directory.Exists(TmpDirectory))
        {
            Directory.CreateDirectory(TmpDirectory);
        }
        bool needCopyData = false;
        string smallManiPath = AbDirectory + "/" + smallManifestFileName;
#if UNITY_IPHONE || UNITY_EDITOR
        AssetFileTool aftIn = GetPackageManifest(smallManifestFileName);
#else
        WWW aftWww = new WWW(Application.streamingAssetsPath + "/android/" + smallManifestFileName);
        yield return aftWww;
        AssetFileTool aftIn = AssetFileTool.LoadFromString(aftWww.text);
#endif
        if (!File.Exists(smallManiPath))
        {
            needCopyData = true;
        }
        else
        {
            AssetFileTool aftOut = AssetFileTool.LoadFromFile(smallManiPath);
            if (aftOut == null)
            {
                File.Delete(smallManiPath);
                needCopyData = true;
                DeleteAllErrorAsset();
            }
            else
            {
                Debug.Log("in version: " + aftIn.version + " out version: " + aftOut.version);
                if (aftOut.version < aftIn.version)
                {
                    List<AssetCell> un, add, odd;
                    AssetFileTool.Compare(aftIn, aftOut, out un, out add, out odd);
                    foreach (var ac in un)
                    {
                        if (File.Exists(AbDirectory + "/" + ac.path))
                        {
                            File.Delete(AbDirectory + "/" + ac.path);
                        }
                    }
                    foreach (var ac in odd)
                    {
                        if (File.Exists(AbDirectory + "/" + ac.path))
                        {
                            File.Delete(AbDirectory + "/" + ac.path);
                        }
                    }
                    CopyCoverage(smallManiPath, TmpDirectory + "/" + smallManifestFileName);
                    needCopyData = true;
                }
            }
        }
        if (needCopyData)
        {
            GameConfig.Instance.dataChange = true;
//#if UNITY_ANDROID
//            System.DateTime dt = System.DateTime.Now;
//            foreach (var ac in aftIn.assetCells)
//            {
//                if (!ac.Key.StartsWith("tabledata/"))
//                {
//                    continue;
//                }
//                TextAsset ta = Resources.Load<TextAsset>("android/" + ac.Value.path);
//                WriteFile(AbDirectory, ac.Key, ta.bytes);
//                Resources.UnloadAsset(ta);
//                if((System.DateTime.Now - dt).TotalSeconds > 1)
//                {
//                    dt = System.DateTime.Now;
//                    yield return 1;
//                }
//            }
//#endif
            string smallManifestPath = AbDirectory + "/" + smallManifestFileName;
            aftIn.SaveToFile(smallManifestPath);
            if (!File.Exists(AbDirectory + "/" + manifestFileName))
            {
                CopyCoverage(smallManifestPath, AbDirectory + "/" + manifestFileName);
            }
            Debug.Log("Write local data over");
        }
        GameConfig.Instance.PreAppVersion = GameConfig.Instance.AppVersion;
        if (copyOnly)
        {
            DataCopyComplete = true;
            state = AssetUpdateState.COMPLETE;
            yield break;
        }
#if TEST
        state = AssetUpdateState.COMPLETE;
        GameConfig.Instance.ResVersion =  GameConfig.Instance.resPackageVersion;
#else
        if (updateMethod == 0)
        {
            state = AssetUpdateState.COMPLETE;
            Debug.Log("Update Asset state: " + state);
        }
        else
        {
            state = AssetUpdateState.SMALL_CHECKING;
            Debug.Log("Update Asset state: " + state);
            StartCoroutine(CheckSmallUpdate());
        }
#endif
        DataCopyComplete = true;
        errorList.Clear();
    }

    /// <summary>
    /// 检测版本更新
    /// </summary>
    /// <param name="url">更新文件的url</param>
    public IEnumerator CheckSmallUpdate()
    {
        yield return StartCoroutine(LoadManifestFromServer(manifestFileName, true));
        if (state == AssetUpdateState.ERROR)
        {
            yield break;
        }
        yield return StartCoroutine(LoadManifestFromServer(smallManifestFileName, false));
        if (state == AssetUpdateState.ERROR)
        {
            yield break;
        }
        localSmallAssetTool = AssetFileTool.LoadFromFile(AbDirectory + "/" + smallManifestFileName);
        if (localSmallAssetTool == null)
        {
            Debug.LogError("small manifest not exist!");
            state = AssetUpdateState.ERROR;
            Debug.Log("Update Asset state: " + state);
            yield break;
        }
        downloadList.Clear();
        AssetFileTool.Compare(localSmallAssetTool, smallServerAssetTool, out unneceList, out lackList, out obbList);
        totalDownloadCount = lackList.Count + obbList.Count;
        totalSize = 0;
        for (int i = lackList.Count - 1; i >= 0; --i)
        {
            if (CheckIfDownload(lackList[i]))
            {
                lackList.RemoveAt(i);
            }
            else
            {
                totalSize += lackList[i].size;
            }
        }
        for (int i = obbList.Count - 1; i >= 0; --i)
        {
            if (CheckIfDownload(obbList[i]))
            {
                obbList.RemoveAt(i);
            }
            else
            {
                totalSize += obbList[i].size;
            }
        }
        loadedCount = 0;
        loadedSize = 0;
        webLoadSize = 0;
        downloadTime = 0;
        state = AssetUpdateState.SMALL_PACKAGE_DOWNLOAD;
        if (totalSize > 0)
        {
            BoxManager.CreateOneDelMsgBox(string.Format(LanguageTool.GetTextNoUpdate(1052), ((float)totalSize / (1 << 20)).ToString("f2")), DownloadSmallConfirm);
        }
        else
        {
            dialogOk = true;
        }
        Debug.Log("Update Asset state: " + state);

    }

    public void DownloadSmallConfirm()
    {
        dialogOk = true;
    }

    public void CheckBigUpdate()
    {
        string manifestPath = AbDirectory + "/" + manifestFileName;
        if (!File.Exists(manifestPath))
        {
            File.Copy(AbDirectory + "/" + smallManifestFileName, manifestPath);
        }
        localAssetTool = AssetFileTool.LoadFromFile(manifestPath);
        downloadList.Clear();
        AssetFileTool.Compare(localAssetTool, serverAssetTool, out unneceList, out lackList, out obbList);
        for (int i = lackList.Count - 1; i >= 0; --i)
        {
            AssetCell ac = lackList[i];
            if (smallServerAssetTool.assetCells.ContainsKey(ac.path))
            {
                if (ac.hashCode == smallServerAssetTool.assetCells[ac.path].hashCode)
                {
                    lackList.RemoveAt(i);
                }
            }
        }
        for (int i = obbList.Count - 1; i >= 0; --i)
        {
            AssetCell ac = obbList[i];
            if (smallServerAssetTool.assetCells.ContainsKey(ac.path))
            {
                if (ac.hashCode == smallServerAssetTool.assetCells[ac.path].hashCode)
                {
                    obbList.RemoveAt(i);
                }
            }
        }


        totalSize = 0;
        for (int i = lackList.Count - 1; i >= 0; --i)
        {
            if (CheckIfDownload(lackList[i]))
            {
                lackList.RemoveAt(i);
            }
            else
            {
                totalSize += lackList[i].size;
            }
        }
        for (int i = obbList.Count - 1; i >= 0; --i)
        {
            if (CheckIfDownload(obbList[i]))
            {
                obbList.RemoveAt(i);
            }
            else
            {
                totalSize += obbList[i].size;
            }
        }
        totalDownloadCount = lackList.Count + obbList.Count;
        loadedCount = 0;
        loadedSize = 0;
        webLoadSize = 0;
        downloadTime = 0;
        if (lackList.Count == 0 && obbList.Count == 0)
        {
            state = AssetUpdateState.BIG_MOVE;
            Debug.Log("Update Asset state: " + state);
            StartCoroutine(MoveBigPack());
        }
        else
        {
            state = AssetUpdateState.BIG_DOWNLOAD;
            Debug.Log("Update Asset state: " + state);
        }
    }

    public bool CheckIfDownload(AssetCell ac)
    {
        bool fileExist = true;
        if (!File.Exists(TmpDirectory + "/" + ac.path))
        {
            fileExist = false;
        }
        else
        {
            FileInfo fi = new FileInfo(TmpDirectory + "/" + ac.path);
            if (fi.Length != ac.size)
            {
                fileExist = false;
                File.Delete(TmpDirectory + "/" + ac.path);
            }
        }
        return fileExist;
    }

    public IEnumerator MoveBigPack()
    {
        DateTime time = DateTime.Now;
        GameConfig.Instance.dataChange = true;
        string[] files = Directory.GetFiles(TmpDirectory);
        int preFixNum = TmpDirectory.Length + 1;
        foreach (var f in files)
        {
            string fileName = f.Substring(preFixNum);
            if (fileName != manifestFileName && fileName != smallManifestFileName && fileName != GetBundleManifestFileName())
            {
                MoveOrReplaceFile(f, AbDirectory + "/" + fileName);
                DateTime t = DateTime.Now;
                if ((t - time).TotalSeconds > 1)
                {
                    time = t;
                    yield return 1;
                }
            }
        }

        string[] ds = Directory.GetDirectories(TmpDirectory);
        foreach (var d in ds)
        {
            string dirName = d.Substring(preFixNum);
            string targetDir = AbDirectory + "/" + dirName;
            if (!Directory.Exists(targetDir))
            {
                Directory.CreateDirectory(targetDir);
            }
            yield return StartCoroutine(MoveRecursive(d, targetDir));
        }

        foreach (var ac in unneceList)
        {
            if (File.Exists(AbDirectory + "/" + ac.path))
            {
                File.Delete(AbDirectory + "/" + ac.path);
            }
        }

        yield return 1;
        //设置IOS下Document文件夹内的东西不自动备份
#if UNITY_IOS
        YCPlugin.IOSBackUP(TmpDirectory+"/"+manifestFileName);
#endif
        CopyCoverage(TmpDirectory + "/" + manifestFileName, AbDirectory + "/" + manifestFileName);
        GameConfig.Instance.IsAssetFull = true;
        GameConfig.Instance.ResVersion = targetVersion.ToString();
        state = AssetUpdateState.COMPLETE;
        Debug.Log("Update Asset state: " + state);
    }

    public IEnumerator MoveSmallPack()
    {
        DateTime time = DateTime.Now;
        GameConfig.Instance.dataChange = true;
        foreach (var ac in unneceList)
        {
            if (File.Exists(AbDirectory + "/" + ac.path))
            {
                File.Delete(AbDirectory + "/" + ac.path);
            }
        }
        if (totalDownloadCount > 0)
        {
            foreach (var ac in smallServerAssetTool.assetCells)
            {
                if (File.Exists(TmpDirectory + "/" + ac.Value.path))
                {
                    MoveOrRelaceFileWithDirectory(TmpDirectory + "/" + ac.Value.path, AbDirectory, ac.Value.path);
                    DateTime t = DateTime.Now;
                    if ((t - time).TotalSeconds > 1)
                    {
                        time = t;
                        yield return 1;
                    }
                }
            }
            CopyCoverage(TmpDirectory + "/" + smallManifestFileName, AbDirectory + "/" + smallManifestFileName);
        }
        state = AssetUpdateState.BIG_CHECKING;
        Debug.Log("Update Asset state: " + state);
        CheckBigUpdate();

        //设置IOS下Document文件夹内的东西不自动备份
#if UNITY_IOS
        YCPlugin.IOSBackUP(TmpDirectory+"/"+manifestFileName);
#endif
    }

    private IEnumerator DownloadAsset(AssetCell ac)
    {
        bool fileExist = true;
        if (!File.Exists(TmpDirectory + "/" + ac.path))
        {
            fileExist = false;
        }
        else
        {
            FileInfo fi = new FileInfo(TmpDirectory + "/" + ac.path);
            if (fi.Length != ac.size)
            {
                fileExist = false;
                File.Delete(TmpDirectory + "/" + ac.path);
            }
        }
        if (!fileExist)
        {
            WWW www = new WWW(baseUrl + "/" + ac.path);
            float prePercent = 0;
            float pretime = Time.unscaledTime;
            while (true)
            {
                if (www.error != null)
                {
                    //download failure
                    Debug.LogError("Download access error " + ac.path + " www error: " + www.error);
                    errorList.Add(ac);
                    www.Dispose();
                    break;
                }
                else if (www.isDone)
                {
                    WriteFile(TmpDirectory, ac.path, www.bytes);
                    www.Dispose();
                    loadedCount++;
                    loadedSize += ac.size;
                    webLoadSize += ac.size;

                    //设置IOS下Document文件夹内的东西不自动备份
#if UNITY_IOS
        YCPlugin.IOSBackUP(TmpDirectory+"/"+manifestFileName);
#endif
                    break;
                }
                else
                {
                    if (www.progress != prePercent)
                    {
                        pretime = Time.unscaledTime;
                        prePercent = www.progress;
                    }
                    else
                    {
                        if (Time.unscaledTime - pretime > 60)
                        {
                            Debug.LogError("Download access error " + ac.path + " www error: " + www.error);
                            errorList.Add(ac);
                            www.Dispose();
                            break;
                        }
                    }
                    yield return 1;
                }
            }
            //yield return www;
            //if (www.error != null)
            //{
            //    //download failure
            //    Debug.LogError("Download access error " + ac.path + " www error: " + www.error);
            //    errorList.Add(ac);
            //    www.Dispose();
            //}
            //else
            //{
            //    WriteFile(TmpDirectory, ac.path, www.bytes);
            //    www.Dispose();
            //    loadedCount++;
            //    loadedSize += ac.size;
            //    webLoadSize += ac.size;
            //}
        }
        else
        {
            loadedCount++;
            loadedSize += ac.size;
        }
        downloadList.Remove(ac);
    }

    private IEnumerator LoadManifestFromServer(string fileName, bool isBig)
    {
        AssetFileTool aft = null;
        string manifestFilePath = TmpDirectory + "/" + fileName;
        if (File.Exists(manifestFilePath))
        {
            aft = AssetFileTool.LoadFromFile(manifestFilePath);
            if (aft == null)
            {
                File.Delete(manifestFilePath);
                DeleteAllTmpAsset();
            }
        }
        AssetFileTool oldAft = null;
        if (aft != null && aft.version != targetVersion)
        {
            oldAft = aft;
        }
        if (aft == null || aft.version != targetVersion)
        {
            WWW www = new WWW(baseUrl + "/" + fileName);
            yield return www;
            if (www.error != null)
            {
                Debug.LogError("down load update file error " + fileName + www.error);
                state = AssetUpdateState.ERROR;
                Debug.Log("Update Asset state: " + state);
                www.Dispose();
                yield break;
            }
            else
            {

                //WriteFile(TmpDirectory,fileName+"_tmp",www.bytes);
                File.WriteAllBytes(manifestFilePath + "_tmp", www.bytes);
                aft = AssetFileTool.LoadFromFile(manifestFilePath + "_tmp");
            }
            www.Dispose();
        }
        if (aft == null)
        {
            Debug.LogError("Server manifest is error ");
            state = AssetUpdateState.ERROR;
            Debug.Log("Update Asset state: " + state);
            yield break;
        }
        if (oldAft != null && oldAft != aft)
        {
            List<AssetCell> unnec, add, obb;
            AssetFileTool.Compare(oldAft, aft, out unnec, out add, out obb);
            foreach (var ac in unnec)
            {
                string path = TmpDirectory + "/" + ac.path;
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
            }
            foreach (var ac in obb)
            {
                string path = TmpDirectory + "/" + ac.path;
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
            }
        }
        if (File.Exists(manifestFilePath + "_tmp"))
        {
            MoveOrReplaceFile(manifestFilePath + "_tmp", manifestFilePath);
        }
        if (isBig)
        {
            serverAssetTool = aft;
        }
        else
        {
            smallServerAssetTool = aft;
        }
    }



#endregion



    private string GetStreamPath()
    {
#if !UNITY_EDITOR && UNITY_ANDROID
        return Application.streamingAssetsPath+"/";
#else
        return @"file:///" + Application.streamingAssetsPath + "/";
#endif
    }

    private AssetFileTool GetPackageManifest(string assetName)
    {
#if UNITY_ANDROID
        AssetFileTool res = AssetFileTool.LoadFromFile(Application.streamingAssetsPath + "/android/" + assetName);
        return res;
#elif UNITY_IPHONE
		AssetFileTool res = AssetFileTool.LoadFromFile(Application.streamingAssetsPath +"/ios/"+ assetName);
		return res;
#else
        return null;
#endif
    }

    private static string GetBundleManifestFileName()
    {
#if UNITY_ANDROID
        return "android";
#elif UNITY_IPHONE
        return "ios";
#else
        return "pc";
#endif
    }

    private void MoveOrReplaceFile(string source, string dest)
    {
        if (File.Exists(dest))
        {
            File.Delete(dest);
        }
        File.Move(source, dest);
    }

    private void MoveOrRelaceFileWithDirectory(string source, string basePath, string path)
    {
        string dest = basePath + "/" + path;
        if (File.Exists(dest))
        {
            File.Delete(dest);
        }
        else
        {
            string[] spl = path.Split('/');
            string p = basePath;
            for (int i = 0; i < spl.Length - 1; ++i)
            {
                p += "/" + spl[i];
                if (!Directory.Exists(p))
                {
                    Directory.CreateDirectory(p);
                }
            }
        }
        File.Move(source, dest);
    }

    private void CopyCoverage(string source, string dest)
    {
        if (File.Exists(dest))
        {
            File.Delete(dest);
        }
        File.Copy(source, dest);
    }

    public IEnumerator MoveRecursive(string source, string target)
    {
        string[] files = Directory.GetFiles(source);
        DateTime dt = DateTime.Now;
        foreach (var f in files)
        {

            string fn = Path.GetFileName(f);
            string targetFile = target + "/" + fn;
            if (File.Exists(targetFile))
            {
                File.Delete(targetFile);
            }
            File.Move(f, targetFile);
            DateTime tmp = DateTime.Now;
            if ((tmp - dt).TotalSeconds > 1)
            {
                dt = tmp;
                yield return 1;
            }
        }
        string[] dirs = Directory.GetDirectories(source);
        foreach (var dir in dirs)
        {
            string sub = GetLastDiretory(dir);
            string t = target + "/" + sub;
            if (!Directory.Exists(t))
            {
#if UNITY_EDITOR
                Debug.Log("create directory " + t);
#endif
                Directory.CreateDirectory(t);
            }
            yield return StartCoroutine(MoveRecursive(dir, t));
        }
#if UNITY_EDITOR
        Debug.Log("delete " + source);
#endif
        Directory.Delete(source);
    }

    private string GetLastDiretory(string path)
    {
        path = path.Replace('\\', '/');
        int idx = path.LastIndexOf('/');
        return path.Substring(idx + 1, path.Length - idx - 1);
    }

    private void DeleteAllTmpAsset()
    {
        string[] files = Directory.GetFiles(TmpDirectory);
        int preFixNum = TmpDirectory.Length + 1;
        foreach (var f in files)
        {
            string fileName = f.Substring(preFixNum);
            if (fileName != manifestFileName && fileName != smallManifestFileName && fileName != GetBundleManifestFileName())
            {
                File.Delete(f);
            }
        }
        string[] ds = Directory.GetDirectories(TmpDirectory);
        foreach (var d in ds)
        {
            DeleteDiretoryFull(d);
        }
    }

    private void DeleteAllErrorAsset()
    {
        string[] files = Directory.GetFiles(AbDirectory);
        int preFixNum = AbDirectory.Length + 1;
        foreach (var f in files)
        {
            string fileName = f.Substring(preFixNum);
            if (fileName != manifestFileName && fileName != smallManifestFileName && fileName != GetBundleManifestFileName())
            {
                File.Delete(f);
            }
        }
        string[] ds = Directory.GetDirectories(AbDirectory);
        foreach (var d in ds)
        {
            DeleteDiretoryFull(d);
        }
    }

    private void DeleteDiretoryFull(string path)
    {
        string[] files = Directory.GetFiles(path);
        foreach (var f in files)
        {
            File.Delete(f);
        }
        string[] ds = Directory.GetDirectories(path);
        foreach (var d in ds)
        {
            DeleteDiretoryFull(d);
        }
        Directory.Delete(path);
    }


    public void WriteFile(string basePath, string path, byte[] data)
    {
        string[] spl = path.Split('/');
        string p = basePath;
        for (int i = 0; i < spl.Length - 1; ++i)
        {
            p += "/" + spl[i];
            if (!Directory.Exists(p))
            {
                Directory.CreateDirectory(p);
            }
        }
        if (spl.Length >= 1)
        {
            File.WriteAllBytes(p + "/" + spl[spl.Length - 1], data);
        }
        else
        {
            Debug.LogError("path error " + basePath);
        }
    }

	public void CopyFileRecursion(string sourcePath,string basePath, string targetPath)
	{
		string[] spl = targetPath.Split('/');
		string p = basePath;
		for (int i = 0; i < spl.Length - 1; ++i)
		{
			p += "/" + spl[i];
			if (!Directory.Exists(p))
			{
				Directory.CreateDirectory(p);
			}
		}
		if (spl.Length >= 1)
		{
			string tp = basePath + "/" + targetPath;
			if (File.Exists (tp)) 
			{
				File.Delete (tp);
			}
			File.Copy (sourcePath, tp);
		}
		else
		{
			Debug.LogError("path error " + basePath);
		}
	}


    public void PrepareTableData(Action prepareOk)
    {
        enabled = true;
        state = AssetUpdateState.COPY_DATA;
        //yield return MoveSmallPack();
        //prepareOk();
        StartCoroutine(_PrepareData(prepareOk));
    }

    private IEnumerator _PrepareData(Action prepareOk)
    {
        yield return StartCoroutine(CopyLocalDataFile(true));
        if (prepareOk != null)
        {
            prepareOk();
        }
    }

    public bool BackgroundLoading
    {
        get
        {
            return state == AssetUpdateState.BIG_DOWNLOAD || GameConfig.Instance.resMethod == ResourceUpdateMethod.SLIENCE_UPDATE && GameConfig.Instance.ResVersion != targetVersion.ToString();
        }
    }

    public bool NeedCopyData()
    {
        CheckDownloadFull();
        return GameConfig.Instance.AppVersion != GameConfig.Instance.PreAppVersion || !File.Exists(AbDirectory + "/" + smallManifestFileName);
    }

    public bool CheckDownloadFull()
    {
        if (!File.Exists(AbDirectory + "/" + smallManifestFileName))
        {
            GameConfig.Instance.IsDownloadOver = false;
            GameConfig.Instance.IsAssetFull = false;
            return false;
        }
        else
        {
            string[] av = GameConfig.Instance.AppVersion.Split('.');
            string[] pav = GameConfig.Instance.PreAppVersion.Split('.');
            if (pav.Length == 0 || av[0] != pav[0] || av[1] != pav[1])
            {
                GameConfig.Instance.IsDownloadOver = false;
                GameConfig.Instance.IsAssetFull = false;
                return false;
            }
        }
        return GameConfig.Instance.IsDownloadOver;
    }
    public void Reset(){
		state = AssetUpdateState.NONE;
	}

#region singleton
    public static AssetUpdateManager instance;
    public static AssetUpdateManager Instance
    {
        get
        {
            return instance;
        }
    }
    void Awake()
    {
        instance = this;

        state = AssetUpdateState.NONE;
        Debug.Log("Update Asset state: " + state);
    }
#endregion singleton
}
